#include "BlockQueue.hpp"
#include "Task.hpp"
#include <ctime>

pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

//C:计算任务    S:保存任务
//当作一个结构体，用于将两个阻塞队列封装到一起
template <class C, class S>
class BlockQueueS
{
public:
    BlockQueue<C> *c_bq;
    BlockQueue<S> *s_bq;
};

void *consumer(void *args)
{
    BlockQueue<CalTask> *cal_bq = static_cast<BlockQueueS<CalTask, SaveTask> *>(args)->c_bq;
    BlockQueue<SaveTask> *save_bq = static_cast<BlockQueueS<CalTask, SaveTask> *>(args)->s_bq;
    while (true)
    {
        //out用于将任务从计算队列中拿出来。
        CalTask out;
        cal_bq->pop(&out);
        std::string result = out();
        //这里加锁是为了防止输出到显示器上的时候，多条消息挤在一起
        pthread_mutex_lock(&lock);
        std::cout << "计算完成: " << result << std::endl;

        //推送计算结果到保存队列中
        SaveTask save(result, Save);
        save_bq->push(save);
        std::cout << "推送结果完成..." << std::endl;
        pthread_mutex_unlock(&lock);
        //防止输出太快
        sleep(1);
    }
}

void *productor(void *args)
{
    BlockQueue<CalTask> *bq = static_cast<BlockQueueS<CalTask, SaveTask> *>(args)->c_bq;
    while (true)
    {
        int x = rand() % 10 + 1;
        int y = rand() % 10;
        int op = rand() % oper.size();//选择一个计算方式
        CalTask t(x, y, oper[op], mymath);
        //将生产出来的任务放进计算队列
        bq->push(t);
        pthread_mutex_lock(&lock);
        std::cout << "生产计算任务: " << t.toTaskString() << std::endl;
        pthread_mutex_unlock(&lock);
        // sleep(1);
    }
}

void *save(void *args)
{
    BlockQueue<SaveTask> *save_bq = static_cast<BlockQueueS<CalTask, SaveTask> *>(args)->s_bq;
    while (true)
    {
        SaveTask out;//用于存储从保存队列中拿出的任务
        save_bq->pop(&out);
        out();
        std::cout << "保存结果完成..." << std::endl;
    }
}


int main()
{
    srand((unsigned int)time(nullptr));
    pthread_t c, p, s;
    BlockQueueS<CalTask, SaveTask> bqs;
    bqs.c_bq = new BlockQueue<CalTask>();
    bqs.s_bq = new BlockQueue<SaveTask>();


    pthread_create(&c, nullptr, consumer, &bqs);//消费者线程
    pthread_create(&p, nullptr, productor, &bqs);//生产者线程
    pthread_create(&s, nullptr, save, &bqs);//保存处理过的任务

    pthread_join(c, nullptr);
    pthread_join(p, nullptr);
    pthread_join(s, nullptr);

    delete bqs.c_bq;
    delete bqs.s_bq;
    return 0;
}